#include "driver_Mobvoi_includes.h"
#include "app_Mobvoi_includes.h"
#include "driver_pwm.h"

#if CONFIG_DRIVER_PWM

#define PWM_GRP0_ENABLE 0
#define PWM_GRP1_ENABLE 0
#define PWM_GRP2_ENABLE 1
#define PWM_GRP3_ENABLE 0

#if CONFIG_PWM_LED
pwm_param_t _pwm_set;
#endif

static void pwm_invalid_enable(pwm_param_t *p_pwm_set, uint8 enable)
{
    return;
}

#if PWM_GRP0_ENABLE
static void pwm_group0_enable(pwm_param_t *p_pwm_set, uint8 enable);
static void pwm_grp0_chnl0_isr(void);
static void pwm_grp0_chnl1_isr(void);
static void pwm_grp0_chnl2_isr(void);
#endif
#if PWM_GRP1_ENABLE
static void pwm_group1_enable(pwm_param_t *p_pwm_set, uint8 enable);
static void pwm_grp1_chnl0_isr(void);
static void pwm_grp1_chnl1_isr(void);
static void pwm_grp1_chnl2_isr(void);
#endif
#if PWM_GRP2_ENABLE
static void pwm_group2_enable(pwm_param_t *p_pwm_set, uint8 enable);
static void pwm_grp2_chnl0_isr(void);
static void pwm_grp2_chnl1_isr(void);
static void pwm_grp2_chnl2_isr(void);
#endif
#if PWM_GRP3_ENABLE
static void pwm_group3_enable(pwm_param_t *p_pwm_set, uint8 enable);
static void pwm_grp3_chnl0_isr(void);
static void pwm_grp3_chnl1_isr(void);
static void pwm_grp3_chnl2_isr(void);
#endif

typedef void (*t_pwm_grp_func)(pwm_param_t *p_pwm_set, uint8 enable);
CONST t_pwm_grp_func pwm_grp_table[] =
    {
#if PWM_GRP0_ENABLE
        &pwm_group0_enable,
#else
        &pwm_invalid_enable,
#endif
#if PWM_GRP1_ENABLE
        &pwm_group1_enable,
#else
        &pwm_invalid_enable,
#endif
#if PWM_GRP2_ENABLE
        &pwm_group2_enable,
#else
        &pwm_invalid_enable,
#endif
#if PWM_GRP3_ENABLE
        &pwm_group3_enable,
#else
        &pwm_invalid_enable,
#endif
};
#define mPWM_Create_Cache_Register(grp) \
    uint32 cache_pwm_reg_##grp = 0
#define mPWM_Assign_Cache_Register(grp, Value) \
    (cache_pwm_reg_##grp = Value)
#define mPWM_Move_Cache_Register(grp, Destination_Register) \
    {                                                       \
        Destination_Register = cache_pwm_reg_##grp;         \
    }
#define mPWM_Public_Setting(grp, p_pwm_set)                                         \
    {                                                                               \
        REG_SYSTEM_0x05 &= ~MSK_SYSTEM_0x05_PWMS##grp##_PWD;                        \
        REG_SYSTEM_0x01 &= ~MSK_SYSTEM_0x01_PWMS##grp##_SEL;                        \
        REG_SYSTEM_0x01 |= (p_pwm_set->clk_sel << SFT_SYSTEM_0x01_PWMS##grp##_SEL); \
    }

#define mPWM_CHNL0_SetReg(grp, chnl, p_pwm_set, enable)                                          \
    {                                                                                            \
        cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PRE_DIV;                                    \
        cache_pwm_reg_##grp |= (p_pwm_set->clk_div << SFT_PWMG##grp##_0x00_PRE_DIV);             \
        if (enable)                                                                              \
        {                                                                                        \
            REG_PWMG##grp##_0x02 = p_pwm_set->cycle;                                             \
            REG_PWMG##grp##_0x03 = p_pwm_set->dutyon;                                            \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_INT_EN;                     \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_MODE;                       \
            cache_pwm_reg_##grp |= (p_pwm_set->mode << SFT_PWMG##grp##_0x00_PWM##chnl##_MODE);   \
            cache_pwm_reg_##grp |= (p_pwm_set->intr << SFT_PWMG##grp##_0x00_PWM##chnl##_INT_EN); \
            cache_pwm_reg_##grp |= MSK_PWMG##grp##_0x00_PWM##chnl##_EN;                          \
        }                                                                                        \
        else                                                                                     \
        {                                                                                        \
            REG_PWMG##grp##_0x02 = 0;                                                            \
            REG_PWMG##grp##_0x03 = 0;                                                            \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_MODE;                       \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_EN;                         \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_INT_EN;                     \
        }                                                                                        \
    }
#define mPWM_CHNL1_SetReg(grp, chnl, p_pwm_set, enable)                                          \
    {                                                                                            \
        cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PRE_DIV;                                    \
        cache_pwm_reg_##grp |= (p_pwm_set->clk_div << SFT_PWMG##grp##_0x00_PRE_DIV);             \
        if (enable)                                                                              \
        {                                                                                        \
            REG_PWMG##grp##_0x05 = p_pwm_set->cycle;                                             \
            REG_PWMG##grp##_0x06 = p_pwm_set->dutyon;                                            \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_INT_EN;                     \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_MODE;                       \
            cache_pwm_reg_##grp |= (p_pwm_set->mode << SFT_PWMG##grp##_0x00_PWM##chnl##_MODE);   \
            cache_pwm_reg_##grp |= (p_pwm_set->intr << SFT_PWMG##grp##_0x00_PWM##chnl##_INT_EN); \
            cache_pwm_reg_##grp |= MSK_PWMG##grp##_0x00_PWM##chnl##_EN;                          \
        }                                                                                        \
        else                                                                                     \
        {                                                                                        \
            REG_PWMG##grp##_0x05 = 0;                                                            \
            REG_PWMG##grp##_0x06 = 0;                                                            \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_MODE;                       \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_EN;                         \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_INT_EN;                     \
        }                                                                                        \
    }
#define mPWM_CHNL2_SetReg(grp, chnl, p_pwm_set, enable)                                          \
    {                                                                                            \
        cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PRE_DIV;                                    \
        cache_pwm_reg_##grp |= (p_pwm_set->clk_div << SFT_PWMG##grp##_0x00_PRE_DIV);             \
        if (enable)                                                                              \
        {                                                                                        \
            REG_PWMG##grp##_0x08 = p_pwm_set->cycle;                                             \
            REG_PWMG##grp##_0x09 = p_pwm_set->dutyon;                                            \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_INT_EN;                     \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_MODE;                       \
            cache_pwm_reg_##grp |= (p_pwm_set->mode << SFT_PWMG##grp##_0x00_PWM##chnl##_MODE);   \
            cache_pwm_reg_##grp |= (p_pwm_set->intr << SFT_PWMG##grp##_0x00_PWM##chnl##_INT_EN); \
            cache_pwm_reg_##grp |= MSK_PWMG##grp##_0x00_PWM##chnl##_EN;                          \
        }                                                                                        \
        else                                                                                     \
        {                                                                                        \
            REG_PWMG##grp##_0x08 = 0;                                                            \
            REG_PWMG##grp##_0x09 = 0;                                                            \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_MODE;                       \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_EN;                         \
            cache_pwm_reg_##grp &= ~MSK_PWMG##grp##_0x00_PWM##chnl##_INT_EN;                     \
        }                                                                                        \
    }

#if PWM_GRP0_ENABLE
void pwm_group0_enable(pwm_param_t *p_pwm_set, uint8 enable)
{
    mPWM_Create_Cache_Register(0);
    mPWM_Assign_Cache_Register(0, REG_PWMG0_0x00);
    /* GRP 0: Public setting */
    mPWM_Public_Setting(0, p_pwm_set);
    /* GRP 0: chnl setting */
    if (p_pwm_set->chnl == 0)
    {
        mPWM_CHNL0_SetReg(0, 0, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 1)
    {
        mPWM_CHNL1_SetReg(0, 1, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 2)
    {
        mPWM_CHNL2_SetReg(0, 2, p_pwm_set, enable);
    }
    else
    {
        FATAL_PRT("PWMG0.chnl\r\n");
    }
    mPWM_Move_Cache_Register(0, REG_PWMG0_0x00);
}
#endif
#if PWM_GRP1_ENABLE
void pwm_group1_enable(pwm_param_t *p_pwm_set, uint8 enable)
{
    mPWM_Create_Cache_Register(1);
    mPWM_Assign_Cache_Register(1, REG_PWMG2_0x00);
    /* GRP 1: Public setting */
    mPWM_Public_Setting(1, p_pwm_set);
    /* GRP 1: chnl setting */
    if (p_pwm_set->chnl == 0)
    {
        mPWM_CHNL0_SetReg(1, 0, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 1)
    {
        mPWM_CHNL1_SetReg(1, 1, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 2)
    {
        mPWM_CHNL2_SetReg(1, 2, p_pwm_set, enable);
    }
    else
    {
        FATAL_PRT("PWMG1.chnl\r\n");
    }
    mPWM_Move_Cache_Register(1, REG_PWMG1_0x00);
}
#endif
#if PWM_GRP2_ENABLE
void pwm_group2_enable(pwm_param_t *p_pwm_set, uint8 enable)
{
    mPWM_Create_Cache_Register(2);
    mPWM_Assign_Cache_Register(2, REG_PWMG2_0x00);
    /* GRP 2: Public setting */
    mPWM_Public_Setting(2, p_pwm_set);
    /* GRP 2: chnl setting */
    if (p_pwm_set->chnl == 0)
    {
        mPWM_CHNL0_SetReg(2, 0, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 1)
    {
        mPWM_CHNL1_SetReg(2, 1, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 2)
    {
        mPWM_CHNL2_SetReg(2, 2, p_pwm_set, enable);
    }
    else
    {
        FATAL_PRT("PWMG2.chnl\r\n");
    }
    mPWM_Move_Cache_Register(2, REG_PWMG2_0x00);
}
#endif
#if PWM_GRP3_ENABLE

void pwm_group3_enable(pwm_param_t *p_pwm_set, uint8 enable)
{
    mPWM_Create_Cache_Register(3);
    mPWM_Assign_Cache_Register(3, REG_PWMG3_0x00);
    /* GRP 3: Public setting */
    mPWM_Public_Setting(3, p_pwm_set);
    /* GRP 3: chnl setting */
    if (p_pwm_set->chnl == 0)
    {
        mPWM_CHNL0_SetReg(3, 0, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 1)
    {
        mPWM_CHNL1_SetReg(3, 1, p_pwm_set, enable);
    }
    else if (p_pwm_set->chnl == 2)
    {
        mPWM_CHNL2_SetReg(3, 2, p_pwm_set, enable);
    }
    else
    {
        FATAL_PRT("PWMG3.chnl\r\n");
    }
    mPWM_Move_Cache_Register(3, REG_PWMG3_0x00);
}
#endif
void pwm_enable(pwm_param_t *p_pwm_set, uint8 enable)
{
    if (p_pwm_set->grp < 4)
    {
        pwm_grp_table[p_pwm_set->grp](p_pwm_set, enable);
    }
    else
    {
        FATAL_PRT("PWM.Grp\r\n");
    }
}
#if (CONFIG_EAR_IN == 1)
void pwm_ear_in_detect(uint8 enable)
{
    pwm_param_t _pwm_set2;
    /*
    pwm_param_t _pwm_set1;
    _pwm_set1.grp = 2;
    _pwm_set1.chnl = 1;  // for sensor
    _pwm_set1.clk_div = 0;
    _pwm_set1.clk_sel = 0;
    _pwm_set1.cycle = 10;
    _pwm_set1.dutyon = 9;
    _pwm_set1.intr = 0;
    _pwm_set1.mode = 1;
*/
    _pwm_set2.grp = 2;
    _pwm_set2.chnl = 0; // for controll
    _pwm_set2.clk_div = 0;
    _pwm_set2.clk_sel = 0;
    _pwm_set2.cycle = 9;
    _pwm_set2.dutyon = 8;
    _pwm_set2.intr = 1; // pwm interrupt enable
    _pwm_set2.mode = 1;

    if (enable)
    {
        //pwm_enable(&_pwm_set1,1);
        gpio_output(app_env_get_pin_num(PIN_earInCs), 1);
        pwm_enable(&_pwm_set2, 1);
        //gpio_config_new(app_env_get_pin_num(PIN_earInCs), GPIO_OUTPUT, GPIO_PULL_UP, GPIO_PERI_FUNC2);  // env cfg
        //gpio_config_new(GPIO26, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_PERI_FUNC2);  // env cfg
    }
    else
    {
        pwm_enable(&_pwm_set2, 0);
        gpio_output(app_env_get_pin_num(PIN_earInCs), 0);
        //pwm_enable(&_pwm_set1,0);
    }
}
#endif

#if CONFIG_PWM_LED
void pwm_led_enable(uint8 enable)
{
    /*
    pwm_param_t _pwm_set1;
    _pwm_set1.grp = 2;
    _pwm_set1.chnl = 0;  // ch0
    _pwm_set1.clk_div = 0;
    _pwm_set1.clk_sel = 0;
    _pwm_set1.cycle = 32;
    _pwm_set1.dutyon = 31;
    _pwm_set1.intr = 1;    // pwm interrupt enable
    _pwm_set1.mode = 2;  // timer mode
*/
    _pwm_set.grp = 2;
    _pwm_set.chnl = 1; // ch1
    _pwm_set.clk_div = 0;
    _pwm_set.clk_sel = 0;
    _pwm_set.cycle = 320;
    _pwm_set.dutyon = 10;
    _pwm_set.intr = 1; // pwm interrupt enable
    _pwm_set.mode = 1; // pwm mode

    if (enable)
    {
        //pwm_enable(&_pwm_set1,1);
        gpio_config_new(27, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_PERI_FUNC2);
        system_ctrl(SYS_CTRL_CMD_PWM7_GPIO_MAPPING, 1); // GPIO27
        pwm_enable(&_pwm_set, 1);
        //gpio_config_new(app_env_get_pin_num(PIN_earInCs), GPIO_OUTPUT, GPIO_PULL_UP, GPIO_PERI_FUNC2);  // env cfg
        //gpio_config_new(GPIO26, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_PERI_FUNC2);  // env cfg
    }
    else
    {
        //pwm_enable(&_pwm_set1,0);
        pwm_enable(&_pwm_set, 0);
        gpio_output(27, 0);
    }
}
#endif

#if PWM_GRP0_ENABLE
void pwm_grp0_chnl0_isr(void){};
void pwm_grp0_chnl1_isr(void){};
void pwm_grp0_chnl2_isr(void){};
#endif
#if PWM_GRP1_ENABLE
void pwm_grp1_chnl0_isr(void){};
void pwm_grp1_chnl1_isr(void){};
void pwm_grp1_chnl2_isr(void){};
#endif
#if PWM_GRP2_ENABLE

volatile uint8 s_pwm_isr_busy = 0;
volatile int8 v_pwm_start_cnt = 0;
__inline uint8 pwm_get_isr_busy(void)
{
    return s_pwm_isr_busy;
}

__inline void pwm_set_isr_busy(uint8 busy)
{
    v_pwm_start_cnt = busy * 2; // for timeout count;
    s_pwm_isr_busy = busy;
}

void pwm_grp2_chnl0_isr(void)
{
    /* Please check gpio of sensor output */
    pwm_set_isr_busy(0);
#if (CONFIG_EAR_IN == 1)
    earin_set_plusdetect_count_pwmisr();
    pwm_ear_in_detect(FALSE);
#endif
};
void pwm_grp2_chnl1_isr(void)
{
#if CONFIG_PWM_LED
    static int_t dir = 1;
    static uint8_t cnt = 0;
    cnt++;
    if (cnt < 20)
        return;
    else
        cnt = 0;
    _pwm_set.intr = 0;
    pwm_enable(&_pwm_set, 0);
    if (dir > 0)
    {
        if (_pwm_set.cycle - 10 > _pwm_set.dutyon)
            _pwm_set.dutyon += 10;
        else
            dir = -1;
    }
    else
    {
        if (_pwm_set.dutyon > 10)
            _pwm_set.dutyon -= 10;
        else
            dir = 1;
    }
    _pwm_set.intr = 1;
    pwm_enable(&_pwm_set, 1);
#endif
}
void pwm_grp2_chnl2_isr(void){};
#endif
#if PWM_GRP3_ENABLE
void pwm_grp3_chnl0_isr(void){};
void pwm_grp3_chnl1_isr(void){};
void pwm_grp3_chnl2_isr(void){};
#endif
void pwm_isr(void)
{
    uint32_t reg;
#if PWM_GRP0_ENABLE
    reg = REG_PWMG0_0x01;
    REG_PWMG0_0x01 = reg; //Clear interrupt flag
    if (reg & MSK_PWMG0_0x01_PWM0_INT)
    {
        pwm_grp0_chnl0_isr();
    }
    if (reg & MSK_PWMG0_0x01_PWM1_INT)
    {
        pwm_grp0_chnl1_isr();
    }
    if (reg & MSK_PWMG0_0x01_PWM2_INT)
    {
        pwm_grp0_chnl2_isr();
    }
#endif
#if PWM_GRP1_ENABLE
    reg = REG_PWMG1_0x01;
    REG_PWMG1_0x01 = reg; //Clear interrupt flag
    if (reg & MSK_PWMG1_0x01_PWM0_INT)
    {
        pwm_grp1_chnl0_isr();
    }
    if (reg & MSK_PWMG1_0x01_PWM1_INT)
    {
        pwm_grp1_chnl1_isr();
    }
    if (reg & MSK_PWMG1_0x01_PWM2_INT)
    {
        pwm_grp1_chnl2_isr();
    }
#endif
#if PWM_GRP2_ENABLE
    reg = REG_PWMG2_0x01;
    REG_PWMG2_0x01 = reg; //Clear interrupt flag
    if (reg & MSK_PWMG2_0x01_PWM0_INT)
    {
        pwm_grp2_chnl0_isr();
    }
    if (reg & MSK_PWMG2_0x01_PWM1_INT)
    {
        pwm_grp2_chnl1_isr();
    }
    if (reg & MSK_PWMG2_0x01_PWM2_INT)
    {
        pwm_grp2_chnl2_isr();
    }
#endif
#if PWM_GRP3_ENABLE
    reg = REG_PWMG3_0x01;
    REG_PWMG3_0x01 = reg; //Clear interrupt flag
    if (reg & MSK_PWMG3_0x01_PWM0_INT)
    {
        pwm_grp3_chnl0_isr();
    }
    if (reg & MSK_PWMG3_0x01_PWM1_INT)
    {
        pwm_grp3_chnl1_isr();
    }
    if (reg & MSK_PWMG3_0x01_PWM2_INT)
    {
        pwm_grp3_chnl2_isr();
    }
#endif
}

#endif
